home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’95 / Hex Abacus / Abacus.c < prev    next >
C/C++ Source or Header  |  1995-06-24  |  8KB  |  335 lines

  1. // Abacus.c
  2. //
  3. // A simple decimal and hexadecimal abacus
  4. // Copyright © 1995 Gary Kacmarcik
  5. // All rights reserved.
  6. //
  7. // Modification History:
  8. //  22 JUN 95  GJK  began at MacHack
  9. //  24 JUN 95  GJK  finished 1st version
  10.  
  11.  
  12. // Magic Cap stuff
  13. #include "Magic.h"
  14. #include "Debug.h"
  15. #include "Package.h"  
  16.  
  17.  
  18. // required by linker
  19. main() {}
  20.  
  21. #undef CURRENTCLASS
  22. #pragma segment Abacus
  23. #define CURRENTCLASS Abacus
  24.  
  25. #define    iHexOnly                MakePackageIndexical(27,1)
  26.  
  27. #define iiAbacusBead              MakePackageFlatIndexical(25)
  28. #define iiAbacus                  MakePackageFlatIndexical(26)
  29.  
  30. #define    NUM_ROWS        16
  31.  
  32. // height of content area of bar
  33. #define    BAR_HEIGHT        0x0C00
  34. #define    BAR_TOTAL        (BAR_HEIGHT + 0x0400)
  35. #define    BAR_TOP_OFFSET    (BAR_TOTAL/2)
  36.  
  37. #define    BEAD_WIDTH        0x1000
  38. #define    BEAD_WIDTH_2    0x0800
  39.  
  40. #define    ROW_SPACING        0x0400
  41.  
  42. Method void
  43. Abacus_SetEndian(ObjectID self, SignedShort value)
  44.     {
  45.     SetField(self, endian, value);
  46.     DirtyContent(iiAbacus);
  47.     RedrawNow();
  48.     }
  49.  
  50. Method void
  51. Abacus_SetBase(ObjectID self, SignedShort value)
  52.     {
  53.     SetField(self, base, 0);
  54.     PlaySound(iMagicSound);
  55.     }
  56.  
  57. Method void
  58. Abacus_Draw(ObjectID self, ObjectID canvas, ObjectID clip)
  59.     {
  60.     Box             bBox, bLine;
  61.     Dot                dOrigin;
  62.     Dot                where;
  63.     Abacus_Fields    fields;
  64.     int                i,j;
  65.     int                nOn, nOff;
  66.     int                nRowValue;
  67.     ObjectID        idText;
  68.     TextRange        range;
  69.     
  70.     InheritedDraw(self, canvas, clip);
  71.     
  72.     // get a ptr to our fields so that we can access them more directly
  73.     ReadFields(self, &fields);
  74.     
  75.     ContentBox(self, &bBox);
  76.     EraseBox(canvas, clip, &bBox);
  77.     
  78.     // draw the wires
  79.     bLine.top = bBox.top;
  80.     bLine.bottom = bBox.bottom;
  81.     bLine.left = bBox.left + BEAD_WIDTH_2 + ROW_SPACING - 0x0100;
  82.     for (i=0; i<NUM_ROWS; i++)
  83.         {
  84.         bLine.right = bLine.left + 0x0300;
  85.         FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
  86.         bLine.left += BEAD_WIDTH + ROW_SPACING;
  87.         }
  88.         
  89.     // draw the horizontal middle bar
  90.     bLine.top = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET;
  91.     bLine.bottom = bLine.top + 0x0100;
  92.     bLine.left = bBox.left;
  93.     bLine.right = bBox.right;
  94.     FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
  95.     bLine.top += 0x0100;
  96.     bLine.bottom = bLine.top + 0x0100;
  97.     FillBox(canvas, clip, &bLine, rgbWhite, pixelCopy);
  98.     bLine.top += 0x0100;
  99.     bLine.bottom = bLine.top + BAR_HEIGHT;
  100.     FillBox(canvas, clip, &bLine, rgbLtGray, pixelCopy);
  101.     bLine.top += BAR_HEIGHT;
  102.     bLine.bottom = bLine.top + 0x0100;
  103.     FillBox(canvas, clip, &bLine, rgbDkGray, pixelCopy);
  104.     bLine.top += 0x0100;
  105.     bLine.bottom = bLine.top + 0x0100;
  106.     FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
  107.     
  108.     // draw the hex digits in the bar
  109.     idText = NewTransient(Text_, nil);
  110.     ReplaceTextWithString(idText, "\p0123456789ABCDEF");
  111.     where.h = bBox.left + BEAD_WIDTH_2 + ROW_SPACING - 0x0200;
  112.     where.v = (bBox.top + bBox.bottom + Ascent(iFatCaps10))/2 - 0x0100;
  113.     range.length = 1;
  114.     for (i=0; i<NUM_ROWS; i++)
  115.         {
  116.         range.start = (&fields.row0)[fields.endian ? i : NUM_ROWS-1-i];
  117.         TextDraw(idText, &range, iFatCaps10, canvas, clip, &where);
  118.         where.h += BEAD_WIDTH + ROW_SPACING;
  119.         }
  120.     Destroy(idText);
  121.  
  122.     // draw the beads
  123.     dOrigin.h = bBox.left + BEAD_WIDTH_2 + ROW_SPACING;
  124.     for (i=0; i<NUM_ROWS; i++)
  125.         {
  126.         nRowValue = (&fields.row0)[fields.endian ? i : NUM_ROWS-1-i];
  127.         
  128.         // each "on" top bead is worth 4
  129.         nOn = (nRowValue & 0x000C) >> 2;
  130.         nOff = 4 - nOn;
  131.         
  132.         // draw "off" top beads
  133.         // start from top and work down
  134.         dOrigin.v = bBox.top + BEAD_WIDTH_2;
  135.         for (j=0; j<nOff; j++)
  136.             {
  137.             DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
  138.             dOrigin.v += BEAD_WIDTH;
  139.             }
  140.         
  141.         // draw "on" top beads
  142.         // start from top of middle bar and work up
  143.         dOrigin.v = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET - BEAD_WIDTH_2;
  144.         for (j=0; j<nOn; j++)
  145.             {
  146.             DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
  147.             dOrigin.v -= BEAD_WIDTH;
  148.             }
  149.         
  150.         // each "on" bottom bead is worth 1
  151.         nOn = nRowValue & 0x0003;
  152.         nOff = 4 - nOn;
  153.         
  154.         // draw "off" bottom beads
  155.         // start from bottom and work up
  156.         dOrigin.v = bBox.bottom - BEAD_WIDTH_2;
  157.         for (j=0; j<nOff; j++)
  158.             {
  159.             DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
  160.             dOrigin.v -= BEAD_WIDTH;
  161.             }
  162.         
  163.         // draw "on" bottom beads
  164.         // start from bottom of middle bar and work down
  165.         dOrigin.v = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET + BAR_TOTAL + BEAD_WIDTH_2;
  166.         for (j=0; j<nOn; j++)
  167.             {
  168.             DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
  169.             dOrigin.v += BEAD_WIDTH;
  170.             }
  171.         
  172.         // next row
  173.         dOrigin.h += BEAD_WIDTH + ROW_SPACING;
  174.         }
  175.     }
  176.  
  177.  
  178. Method void
  179. Abacus_Touch(ObjectID self, ObjectID touchInput)
  180.     {
  181.     Dot                where;
  182.     Box                bBox;
  183.     int                i;
  184.     int                nTappedRow;
  185.     int                nRowLeft, nRowRight;
  186.     int                nBarTop;
  187.     int                nValue;
  188.     int                nBeadNum;
  189.     Abacus_Fields    *fields;
  190.     
  191.     // find where the tap was
  192.     LatestPoint(touchInput, &where);
  193.     
  194.     ContentBox(self, &bBox);
  195.     fields = BeginModifyFields(self);
  196.  
  197.     // first, find which row (if any) the tap was on
  198.     nTappedRow = -1;
  199.     nRowLeft = bBox.left + ROW_SPACING;
  200.     nRowRight = nRowLeft + BEAD_WIDTH;
  201.     for (i=0; i<NUM_ROWS; i++)
  202.         {
  203.         if (where.h > nRowLeft && where.h < nRowRight)
  204.             nTappedRow = i;
  205.         nRowLeft += BEAD_WIDTH + ROW_SPACING;
  206.         nRowRight = nRowLeft + BEAD_WIDTH;
  207.         }
  208.     // bail if the tap wasn't within a row
  209.     if (nTappedRow == -1)
  210.         return;
  211.     if (!fields->endian)
  212.         nTappedRow = NUM_ROWS-1 - nTappedRow;
  213.     
  214.     // was the click in the horizontal bar?
  215.     nBarTop = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET;
  216.     if (where.v > nBarTop && where.v < (nBarTop + BAR_TOTAL))
  217.         // we don't do anything here
  218.         return;
  219.             
  220.     nValue = (&fields->row0)[nTappedRow];
  221.     if ((nBeadNum = FindBead(where, nValue, &bBox)) != -1)
  222.         {
  223.         int        nNewValue;
  224.         int        nOnBeads;
  225.         ObjectID    idSound;
  226.         
  227.         // check upper beads
  228.         if (nBeadNum < 4)
  229.             {
  230.             nOnBeads = (nValue & 0x000C) >> 2;
  231.             if ((4-nOnBeads) > nBeadNum)
  232.                 {    // down (adding 4-beads)
  233.                 idSound = iMagicSound;
  234.                 nNewValue = nValue + 4 * (4-nOnBeads-nBeadNum);
  235.                 }
  236.             else
  237.                 {    // up (subtracting 4-beads)
  238.                 idSound = iMagicSound;
  239.                 nNewValue = nValue - 4 * (nOnBeads+nBeadNum-3);
  240.                 }
  241.             }
  242.             
  243.         // check lower beads
  244.         else
  245.             {
  246.             nOnBeads = (nValue & 0x0003);
  247.             nBeadNum -= 4;
  248.             if (nOnBeads > nBeadNum)
  249.                 {    // down (subtracting 1-beads)
  250.                 idSound = iMagicSound;
  251.                 nNewValue = nValue - (nOnBeads-nBeadNum);
  252.                 }
  253.             else
  254.                 {    // up (adding 1-beads)
  255.                 idSound = iMagicSound;
  256.                 nNewValue = nValue + (4-nOnBeads+nBeadNum-3);
  257.                 }
  258.             }
  259.             
  260.         UpdateRow(fields, nTappedRow, nNewValue);
  261.  
  262. //        PlaySound(idSound);
  263.         DirtyContent(self);
  264.         RedrawNow();
  265.         }
  266.     EndModify(self);
  267.     }
  268.  
  269.  
  270. // update the row with the given value
  271. // this assumes that it is called in a BeginModifyFields/EndModify struct
  272. // make sure that the carry beads get propagated
  273. Private void
  274. UpdateRow(Abacus_Fields    *fields, int row, int newVal)
  275.     {
  276.     (&fields->row0)[row] = (newVal & 0x000F);
  277.     for (row++; newVal > 0x000F && row < NUM_ROWS; row++)
  278.         {
  279.         newVal = (&fields->row0)[row] + 1;
  280.         (&fields->row0)[row] = (newVal & 0x000F);
  281.         }
  282.     }
  283.     
  284.     
  285. // given a point and a row, this will return the bead that contains the point.
  286. Private int
  287. FindBead(Dot where, int nValue, Box *bBox)
  288.     {
  289.     int                nTapOnTopPart;
  290.     int                nBeadTop;
  291.     int                nTop, nBottom;
  292.     int                nBase;
  293.     int                i,j;
  294.     
  295.     // was the click in the upper or lower portion of the abacus?
  296.     if (where.v < (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET)
  297.         {
  298.         nTapOnTopPart = 1;
  299.         nTop = bBox->top;
  300.         nBottom = (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET;
  301.         nValue = (nValue & 0x000C) >> 2;
  302.         nBase = 0;
  303.         }
  304.     else
  305.         {
  306.         nTapOnTopPart = 0;
  307.         nTop = (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET + BAR_TOTAL;
  308.         nBottom = bBox->bottom;
  309.         // we reverse the "on" and "off" bits here so that the following
  310.         // calculations can be shared with the upper part
  311.         nValue = 4 - (nValue & 0x0003);
  312.         nBase = 4;
  313.         }
  314.         
  315.     // at this point, nValue contains the # of beads at the bottom part of
  316.     // the tapped section of the abacus.  for the upper part, this corresponds
  317.     // to "on" beads, for the lower part, this corresponds to "off" beads.
  318.     
  319.     // check the upper beads
  320.     nBeadTop = nTop;
  321.     for (i=0; i<(4-nValue) ;i++, nBeadTop += BEAD_WIDTH)
  322.         if (where.v > nBeadTop && where.v < nBeadTop + BEAD_WIDTH)
  323.             return nBase + i;
  324.     
  325.     // check the lower beads
  326.     nBeadTop = nBottom - BEAD_WIDTH;
  327.     for (i=3, j=0; j<nValue; i--, j++, nBeadTop -= BEAD_WIDTH)
  328.         if (where.v > nBeadTop && where.v < nBeadTop + BEAD_WIDTH)
  329.             return nBase + i;
  330.     
  331.     return -1;
  332.     }
  333.     
  334. #undef CURRENTCLASS
  335.